/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * TrainingBook
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <Activity.hpp>
#include <math.h>

///@brief Default constructor
Activity::Activity()
{

}

///@brief Copy constructor
///@param a Activity object to copy
Activity::Activity(const Activity& a)
{
    header = a.header;
    history = a.history;
}

///@brief Destructor
Activity::~Activity()
{

}

///@brief Overload of = operator
///@param a Activity object to copy
Activity &Activity::operator=(const Activity& a)
{
    header = a.header;
    history = a.history;
    return (*this);
}

///@brief Load track from a GPX file
///@param path Path to the GPX file to read
void Activity::LoadGPX(std::string path)
{
    ///@warning This function has to be entrely rewritten
    /*trace.Clear();
    trace.ReadGPX(path);
    header.SetDistance(trace.Length());
    header.SetDuration(trace.GetDuration());
    header.SetDateTime(trace.GetStartDate());
    trackIsEmbedded = true;*/
}

///@brief Return distance value
double Activity::GetDistance()
{
    return header.GetDistance();
}

///@brief Return duration value 
Duration Activity::GetDuration()
{
    return header.GetDuration();
}

///@brief Return date time value
DateTime Activity::GetDateTime() const
{
    return header.GetDateTime();
}


///@brief Return FC Max value
int Activity::GetFCMax()
{
    return header.GetFCMax();
}

///@brief Return FC Moy value
int Activity::GetFCMoy()
{
    return header.GetFCMoy();
}

///@brief Return the type of activity
std::string Activity::GetActivityType()
{
    return header.GetActivityType();
}

///@brief Return the location value
std::string Activity::GetLocation()
{
    return header.GetLocation();
}

///@brief Return the comments
std::string Activity::GetComments()
{
    return header.GetComments();
}

///@brief Return the consumed calories
///@return number of consumed Calories
unsigned int Activity::GetCalories()
{
    return header.GetCalories();
}
///@brief Return the trained quality
///@return Trained quality name
std::string Activity::GetQuality()
{
    return header.GetQuality();
}

///@brief Return training load
double Activity::GetTrainingLoad()
{
    double res = 0.;
    res += header.GetTimeInZone(1) * COEF_FOR_I1;
    res += header.GetTimeInZone(2) * COEF_FOR_I2;
    res += header.GetTimeInZone(3) * COEF_FOR_I3;
    res += header.GetTimeInZone(4) * COEF_FOR_I4;
    res += header.GetTimeInZone(5) * COEF_FOR_I5;
    res += header.GetTimeInZone(6) * COEF_FOR_I6;
    res += header.GetTimeInZone(7) * COEF_FOR_I7;
    res *= 6;
    return res;
}

///@brief Return activity header
ActivityHeader Activity::GetHeader()
{
    return header;
}

///@brief Return activity history
ActivityHistory Activity::GetHistory()
{
    return history;
}

///@brief Return the average cadence
unsigned int Activity::GetAverageCadence()
{
    return header.GetAverageCadence();
}

///@brief Return the average power
double Activity::GetAveragePower()
{
    return header.GetAveragePower();
}

/// SETTERS

///@brief Set the type of activity
///@param s String containing the type of activity
void Activity::SetActivityType(std::string s)
{
    header.SetActivityType(s);
}

///@brief Set the distance value
///@param dist Distance value to be set
void Activity::SetDistance(double dist)
{
    header.SetDistance(dist);
}

///@brief Set the duration value
///@param d Duration value to be set
void Activity::SetDuration(Duration d)
{
    header.SetDuration(d);
}

///@brief Set FC Moy value
///@param DfcMoy FC Moy value to be set
void Activity::SetFCMoy(int DfcMoy)
{
    header.SetFCMoy(DfcMoy);
}

///@brief Set FC Max value
///@param DfcMax FC Max value to be set
void Activity::SetFCMax(int DfcMax)
{
    header.SetFCMax(DfcMax);
}

///@brief Set the date value
///@param d Date value to be set
void Activity::SetDateTime(DateTime d)
{
    header.SetDateTime(d);
}

///@brief Set the location value
///@param Dlocation Location value to be set
void Activity::SetLocation(std::string Dlocation)
{
    header.SetLocation(Dlocation);
}

///@brief Set the comments
///@param Dcomments Comments value to be set
void Activity::SetComments(std::string Dcomments)
{
    header.SetComments(Dcomments);
}

///@brief Set the consumed calories value
///@param Dcalories Calories value to bet set
void Activity::SetCalories(unsigned int Dcalories)
{
    header.SetCalories(Dcalories);
}

///@brief Set the trained quality
///@param Dquality quality value to be set
void Activity::SetQuality(std::string Dquality)
{
    header.SetQuality(Dquality);
}

///@brief Set the distance from values contained in the GPS track
void Activity::SetDistanceFromTrack()
{
    ///@brief This function should be rewritten
    //header.SetDistance(trace.Length());
}

///@brief Set the duration from values contained in the GPS track
void Activity::SetDurationFromTrack()
{
    ///@warning THis function should be rewritten
    //header.SetDuration(trace.GetDuration());
}

///@brief Set the header of the activity
///@param dheader ACtivityHeader object to be set
void Activity::SetHeader(ActivityHeader dheader)
{
    header = dheader;
}

///@brief Set the history of the activity
///@param dhistory ActivityHistory object to be set
void Activity::SetHistory(ActivityHistory dhistory)
{
    history = dhistory;
}

///@brief Read informations from a XML node
///@param currentNode Pointer to XML node to read
void Activity::ReadXMLNode(xmlpp::Node* currentNode)
{
    xmlpp::Node::NodeList listNodes = currentNode->get_children();
    for(xmlpp::Node::NodeList::iterator iterNodes = listNodes.begin(); iterNodes != listNodes.end(); ++iterNodes)
    {
        xmlpp::Node* nodeToAdd = dynamic_cast<const xmlpp::Node*>(*iterNodes);
        if(nodeToAdd->get_name() == "Date")
        ///@brief Readind the date
        {
            std::string dateString = ((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content();
            DateTime tempDate;
            tempDate.SetWithFormat(dateString, "yyyy-mm-dd--hh-nn-ss");
            header.SetDateTime(tempDate);
        }
        else if(nodeToAdd->get_name() == "Location")
        ///@brief Reading the location
        {
            header.SetLocation(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
        }
        else if(nodeToAdd->get_name() == "TypeActivity")
        ///@brief Reading the type of activity
        {
            header.SetActivityType(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
        }
        else if(nodeToAdd->get_name() == "Distance")
        ///@brief Reading the distance
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            double tempDistance;
            stream >> tempDistance;
            header.SetDistance(tempDistance);
        }
        else if(nodeToAdd->get_name() == "Duration")
        ///@brief Reading the duration
        {
            std::string durationString = ((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content();
            Duration tempDuration;
            tempDuration.SetWithFormat(durationString, "h:nn:ss" );
            header.SetDuration(tempDuration);
        }
        else if(nodeToAdd->get_name() == "FCMax")
        ///@brief Reading the FC Max
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempFCMax;
            stream >> tempFCMax;
            header.SetFCMax(tempFCMax);
        }
        else if(nodeToAdd->get_name() == "FCMoy")
        ///@brief Reading the FC Moy
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempFCMoy;
            stream >> tempFCMoy;
            header.SetFCMoy(tempFCMoy);
        }
        ///@warning The history should be read from the file
        /*else if(nodeToAdd->get_name() == "Track")
        ///@brief Reading the GPS track
        {
            xmlpp::Node::NodeList listTrackSeg = nodeToAdd->get_children();
            for(xmlpp::Node::NodeList::iterator jiter = listTrackSeg.begin(); jiter != listTrackSeg.end(); ++jiter)
            {
                xmlpp::Node* isTrackSeg = dynamic_cast<const xmlpp::Node*>(*jiter);
                if(isTrackSeg->get_name() == "trkseg")
                {
                    TrackSegment newTrack;
                    if(newTrack.ReadXMLNode(isTrackSeg))
                        trace.AddSegment(newTrack);
                }
            }
        }
        else if(nodeToAdd->get_name() == "TrackIsEmbedded")
        ///@brief Reading the boolean indicating if the track is embedded in the activity
        {
            std::string value = ((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content();
            if(value == "yes")
                trackIsEmbedded = true;
            else
                trackIsEmbedded = false;
        }
        else if(nodeToAdd->get_name() == "TrackFile")
        ///@brief Reading the name of the file containing the GPS track
        {
            trackFile = ((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content();
        }*/
        else if(nodeToAdd->get_name() == "Comments")
        ///@brief Reading the comments
        {
            header.SetComments(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
        }
        else if(nodeToAdd->get_name() == "Calories")
        ///@brief Reading the consumed calories
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempCalories;
            stream >> tempCalories;
            header.SetCalories(tempCalories);
        }
        else if(nodeToAdd->get_name() == "Quality")
        ///@brief Reading the trained quality
        {
            header.SetQuality(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
        }
        else if(nodeToAdd->get_name() == "MinutesInI1")
        ///@brief Reading the number of minutes spent in intensity 1
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempIntensity;
            stream >> tempIntensity;
            header.SetTimeInZone(1, tempIntensity);
        }
        else if(nodeToAdd->get_name() == "MinutesInI2")
        ///@brief Reading the number of minutes spent in intensity 2
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempIntensity;
            stream >> tempIntensity;
            header.SetTimeInZone(2, tempIntensity);
        }
        else if(nodeToAdd->get_name() == "MinutesInI3")
        ///@brief Reading the number of minutes spent in intensity 3
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempIntensity;
            stream >> tempIntensity;
            header.SetTimeInZone(3, tempIntensity);
        }
        else if(nodeToAdd->get_name() == "MinutesInI4")
        ///@brief Reading the number of minutes spent in intensity 4
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempIntensity;
            stream >> tempIntensity;
            header.SetTimeInZone(4, tempIntensity);
        }
        else if(nodeToAdd->get_name() == "MinutesInI5")
        ///@brief Reading the number of minutes spent in intensity 5
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempIntensity;
            stream >> tempIntensity;
            header.SetTimeInZone(5, tempIntensity);
        }
        else if(nodeToAdd->get_name() == "MinutesInI6")
        ///@brief Reading the number of minutes spent in intensity 6
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempIntensity;
            stream >> tempIntensity;
            header.SetTimeInZone(6, tempIntensity);
        }
        else if(nodeToAdd->get_name() == "MinutesInI7")
        ///@brief Reading the number of minutes spent in intensity 7
        {
            std::istringstream stream(((xmlpp::TextNode*)(*((nodeToAdd->get_children()).begin())))->get_content());
            unsigned int tempIntensity;
            stream >> tempIntensity;
            header.SetTimeInZone(7, tempIntensity);
        }
    }
}

///@brief Write to XML file
///@param fout out stream to XML file
void Activity::WriteXML(std::ofstream &fout)
{
    fout << "      <Date>" << header.GetDateTime() << "</Date>\n";
    if(header.GetLocation().length() != 0)
        fout << "     <Location>" << header.GetLocation() << "</Location>\n";
    if(header.GetActivityType().length() != 0)
        fout << "      <TypeActivity>" << header.GetActivityType() << "</TypeActivity>\n";
    fout << "      <Distance>" << header.GetDistance() << "</Distance>\n";
    fout << "      <Duration>" << header.GetDuration() << "</Duration>\n";
    fout << "      <FCMoy>" << header.GetFCMoy() << "</FCMoy>\n";
    fout << "      <FCMax>" << header.GetFCMax() << "</FCMax>\n";
    ///@warning Track should be written in the file
    /*if(!trace.Empty())
    {
        fout << "\t\t\t<Track>\n";
        trace.WriteXML(fout);
        fout << "\t\t\t</Track>\n";
    }
    if(trackIsEmbedded)
    {
        fout << "\t\t\t<TrackIsEmbedded>yes</TrackIsEmbedded>\n";

    }
    else
    {
        fout << "\t\t\t<TrackIsEmbedded>no</TrackIsEmbedded>\n";
        fout << "\t\t\t<TrackFile>" << trackFile << "</TrackFile>\n";
    }*/
    
    if(header.GetComments().length() != 0)
        fout << "      <Comments>" << header.GetComments() << "</Comments>\n";
    fout << "      <Calories>" << header.GetCalories() << "</Calories>\n";
    if(header.GetQuality().length() != 0)
        fout << "      <Quality>" << header.GetQuality() << "</Quality>\n";
    fout << "      <MinutesInI1>" << header.GetTimeInZone(1) << "</MinutesInI1>\n";
    fout << "      <MinutesInI2>" << header.GetTimeInZone(2) << "</MinutesInI2>\n";
    fout << "      <MinutesInI3>" << header.GetTimeInZone(3) << "</MinutesInI3>\n";
    fout << "      <MinutesInI4>" << header.GetTimeInZone(4) << "</MinutesInI4>\n";
    fout << "      <MinutesInI5>" << header.GetTimeInZone(5) << "</MinutesInI5>\n";
    fout << "      <MinutesInI6>" << header.GetTimeInZone(6) << "</MinutesInI6>\n";
    fout << "      <MinutesInI7>" << header.GetTimeInZone(7) << "</MinutesInI7>\n";
}

///@brief Recompute the average power from history
///@return true if the computed average power is different than the existing one
///@warning This function returns nan value in some cases
bool Activity::RecomputeAveragePower()
{
    double prevPower = header.GetAveragePower();
    header.SetAveragePower(history.ComputeAveragePower());
    if(fabs(header.GetAveragePower()-prevPower) > 0.01)
    {
        return true;
    }
    return false;
}


///@brief Overload of < operator to sort activities depending on their date
///@param a1 Activity left hand operand
///@param a2 Activity right hand operand
bool operator < (const Activity& a1, const Activity & a2)
{
    return (a1.GetDateTime() < a2.GetDateTime());
}